#ifndef _CONTROL_UNIT_H_
#define _CONTROL_UNIT_H_

#include "systemc.h"

#define N_REG 32

/*
 * Try to include all CPU logic here. If so, s/control_unit/cpu/g
 */
class control_unit : sc_module
{
  /* Program Counter */
  sc_lv<32> _pc;

  /* Instruction Register */
  sc_lv<32> _ir;

  /* Register File */
  sc_lv<32> _rf[N_REG];

  /* Mem Data */
  sc_lv<32> _md;

  /* ALU operands */
  sc_lv<32> _a;
  sc_lv<32> _b;
  sc_lv<32> _alu_out;
  sc_lv<32> _target;

public:
  sc_in< bool >       clk;
  sc_in< sc_logic >   rst; // inhibited right now

  /* MEM HIERARCHY */
  /* CACHE CONTROLLER */
  sc_in< sc_logic >   cc_rdy;
  sc_in< sc_lv<32> >  cc_dout;
    
  sc_out< sc_logic >  cc_rw;
  sc_out< sc_logic >  cc_cs;
  sc_out< sc_lv<32> > cc_addr;
  sc_out< sc_lv<32> > cc_din;

  /* Monitoring Section*/
  /* STATE */
  sc_out< sc_lv<4> >  state;
  /* CPU REGS */
  sc_out< sc_lv<32> > pc;
  sc_out< sc_lv<32> > ir;
  sc_out< sc_lv<32> > r0; // r0 in rf
  sc_out< sc_lv<32> > r1;
  sc_out< sc_lv<32> > r2;
  sc_out< sc_lv<32> > r3;
  sc_out< sc_lv<32> > r4;
  sc_out< sc_lv<32> > r5; // r5 in rf
  sc_out< sc_lv<32> > md;
  sc_out< sc_lv<32> > a;
  sc_out< sc_lv<32> > b;
  sc_out< sc_lv<32> > alu_out;
  sc_out< sc_lv<32> > target;
  /* End Monitoring Section */

  SC_CTOR (control_unit) 
    : _pc(0), _ir(0), _a(0), _b(0), _alu_out(0), _md(0), _target(0)
  {
    /* BEGIN initialization */
    for (int i = 0; i < N_REG; i++)
      _rf[i] = 0;

    cc_rw.initialize(SC_LOGIC_0);
    cc_cs.initialize(SC_LOGIC_0);
    cc_addr.initialize(0);
    cc_din.initialize(0);

    state.initialize(0);
    pc.initialize(_pc);
    ir.initialize(_ir);
    md.initialize(_md);
    a.initialize(_a);
    b.initialize(_b);
    alu_out.initialize(_alu_out);
    target.initialize(_target);

    r0.initialize(_rf[0]);
    r1.initialize(_rf[1]);
    r2.initialize(_rf[2]);
    r3.initialize(_rf[3]);
    r4.initialize(_rf[4]);
    r5.initialize(_rf[5]);
    /* END initialization */

    SC_THREAD (fsm);
    sensitive << clk.pos() << rst.pos();

    SC_METHOD (fsm_logic);
    sensitive << state;
    dont_initialize();

    SC_METHOD (reg_refresh);
    sensitive << clk.neg(); // refresh arrives too late :/
    dont_initialize();
  }

  void fsm(void);
  void fsm_logic(void);
  void reg_refresh(void);

};

#endif /* _CONTROL_UNIT_H_ */
